#include "buffer.h"
#include <cstdarg>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>

WriteReadBuffer::WriteReadBuffer(int buffer_size)
{
    m_buffer_vec.resize(buffer_size);
    m_data_start_index = 0;
    m_data_end_index   = 0;
}

int WriteReadBuffer::ExpendBuffer(int expend_size)
{
    int                  total_size = m_buffer_vec.size();
    std::vector<uint8_t> buffer(total_size + expend_size);
    memcpy(buffer.data(), m_buffer_vec.data() + m_data_start_index, BufferDataSize());
    m_buffer_vec.swap(buffer);

    m_data_end_index   = BufferDataSize();
    m_data_start_index = 0;
    return 0;
}

uint8_t * WriteReadBuffer::BufferData()
{
    return m_buffer_vec.data() + m_data_start_index;
}

uint8_t * WriteReadBuffer::BufferEnd()
{
    return m_buffer_vec.data() + m_data_end_index;
}

int WriteReadBuffer::BufferFreeSize() const
{
    return (int(m_buffer_vec.size()) - m_data_end_index);
}

int WriteReadBuffer::BufferDataSize() const
{
    return (m_data_end_index - m_data_start_index);
}

void WriteReadBuffer::MovePosition(int size)
{
    m_data_end_index += size;
}

void WriteReadBuffer::RemoveSize(int size)
{
    m_data_start_index += size;
    if (m_data_start_index == m_data_end_index)
    {
        m_data_end_index   = 0;
        m_data_start_index = 0;
    }
}

void WriteReadBuffer::PushDataToBuffer(const uint8_t * data, int length)
{
    int free_size = BufferFreeSize();
    if (free_size >= length)
    {
        memcpy(m_buffer_vec.data() + m_data_end_index, data, length);
        m_data_end_index += length;
    }
    else
    {
        int expend_size = free_size + s_buffer_size;
        if (expend_size < length)
        {
            expend_size = length + s_buffer_size;
        }
        ExpendBuffer(expend_size);
        memcpy(m_buffer_vec.data() + m_data_end_index, data, length);
        m_data_end_index += length;
    }
}

WriteBuffer::WriteBuffer() : WriteReadBuffer()
{
}

WriteBuffer::WriteBuffer(int size):WriteReadBuffer(size)
{

}

void WriteBuffer::WriteUint8(uint8_t data)
{
    WriteN(&data, sizeof(data));
}

void WriteBuffer::WriteUint16(uint16_t data)
{
    WriteN((uint8_t *)&data, sizeof(data));
}

void WriteBuffer::WriteUint32(uint32_t data)
{
    WriteN((uint8_t *)&data, sizeof(data));
}

void WriteBuffer::WriteUint64(uint64_t data)
{
    WriteN((uint8_t *)&data, sizeof(data));
}

void WriteBuffer::WriteN(const uint8_t * data, int size)
{
    PushDataToBuffer((uint8_t *)data, size);
}

void WriteBuffer::WriteString(const std::string & content)
{
    const uint8_t * data = (const uint8_t*)content.c_str();
    PushDataToBuffer(data, static_cast<int>(content.size()));
}

void WriteBuffer::WriteString(const char * data, int length)
{
    const uint8_t * ptr = reinterpret_cast<const uint8_t *>(data);
    PushDataToBuffer(ptr, length);
}

void WriteBuffer::PrintFmt(const char * fmt, ...)
{
    va_list ap = {};
    va_start(ap, fmt);
    char * p = nullptr;
    const int len = vasprintf(&p, fmt, ap);
    va_end(ap);
    p[len] = '\0';
    WriteString(p, len);
    free(p);
}

ReferenceBuffer::ReferenceBuffer(const uint8_t * data, int data_length) : m_data_ptr(data), m_data_length(data_length)
{
}

ReferenceBuffer::~ReferenceBuffer()
{
    m_data_ptr    = nullptr;
}

const uint8_t * ReferenceBuffer::Buffer() const
{
    return m_data_ptr;
}

int ReferenceBuffer::BufferSize() const
{
    return m_data_length;
}
